package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningRecordFormDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.mq.LearningRecordProducer;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService2;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 学习记录表 服务实现类
 * 高并发优化 -- 变同步为异步 mq消息队列
 * </p>
 *
 * @author huanmo
 * @since 2025-09-17
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LearningRecordServiceImpl2 extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService2 {

    private final ILearningLessonService lessonService;

    private final CourseClient courseClient;

    private final LearningRecordProducer learningRecordProducer;

    private final RabbitMqHelper mqHelper;

    @Override
    @Transactional
    public void addLearningRecord(LearningRecordFormDTO recordDTO) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.处理学习记录
        if (SectionType.of(recordDTO.getSectionType()) == SectionType.VIDEO) {
            // 2.1.处理视频
            learningRecordProducer.sendLearningRecord(recordDTO);
        } else {
            boolean finished = false;
            // 2.2.处理考试
            finished = handleExamRecord(userId, recordDTO);
            // 更新课表学习进度
            handleLearningLessonsChanges(recordDTO, finished);
        }
    }

    /**
     * 添加异步处理方法，供MQ消费者调用
     * 在MQ监听器调用的方法中，UserContext.getUser() 无法直接获取到用户信息，因为：
     *      线程上下文丢失：MQ消费者在单独的线程中执行，用户上下文信息没有传递过来
     *      异步调用：MQ消息处理是异步的，原始请求的ThreadLocal信息不会自动传递
     */
    @Transactional
    public void handleVideoRecordAsync(LearningRecordFormDTO recordDTO) {
        Long userId = UserContext.getUser();
        log.info("处理异步学习记录，用户ID: {}", userId);
        // 处理视频记录
        boolean finished = handleVideoRecord(userId, recordDTO);
        // 处理课表更新
        handleLearningLessonsChanges(recordDTO, finished);
        log.info("更新课表学习进度成功");
        // 保存积分明细记录（确保学习进度更新成功后再加分）
        try {
            // 发送学习完成消息到MQ
            mqHelper.send(
                    MqConstants.Exchange.LEARNING_EXCHANGE,
                    MqConstants.Key.LEARN_SECTION,
                    userId
            );
            log.info("发送学习积分消息成功，用户ID: {}, 积分: 10", userId);
        } catch (Exception e) {
            log.error("发送学习积分消息失败，用户ID: {}", userId, e);
        }
    }

    private void handleLearningLessonsChanges(LearningRecordFormDTO recordDTO, boolean finished) {
        // 1.查询课表
        LearningLesson lesson = lessonService.getById(recordDTO.getLessonId());
        if (lesson == null) {
            throw new BizIllegalException("课程不存在，无法更新数据！");
        }
        // 2.判断是否有新的完成小节
        boolean allLearned = false;
        if (finished) {
            // 3.如果有新完成的小节，则需要查询课程数据
            CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if (cInfo == null) {
                throw new BizIllegalException("课程不存在，无法更新数据！");
            }
            // 4.比较课程是否全部学完：已学习小节 >= 课程总小节
            allLearned = lesson.getLearnedSections() + 1 >= cInfo.getSectionNum();
        }
        // 5.更新课表
        lessonService.lambdaUpdate()
                .set(lesson.getLearnedSections() == 0, LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED.getValue())
                .set(!finished, LearningLesson::getLatestSectionId, recordDTO.getSectionId())
                .set(!finished, LearningLesson::getLatestLearnTime, recordDTO.getCommitTime())
                .setSql(finished, "learned_sections = learned_sections + 1")
                .eq(LearningLesson::getId, lesson.getId())
                .update();
    }

    public boolean handleVideoRecord(Long userId, LearningRecordFormDTO recordDTO) {
        // 1.查询旧的学习记录
        LearningRecord old = queryOldRecord(recordDTO.getLessonId(), recordDTO.getSectionId());
        // 2.判断是否存在
        if (old == null) {
            // 3.不存在，则新增
            // 3.1.转换PO
            LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
            // 3.2.填充数据
            record.setUserId(userId);
            // 3.3.写入数据库
            boolean success = save(record);
            if (!success) {
                throw new DbException("新增学习记录失败！");
            }
            return false;
        }
        // 4.存在，则更新
        // 4.1.判断是否是第一次完成
        boolean finished = !old.getFinished() && recordDTO.getMoment() * 2 >= recordDTO.getDuration();
        // 4.2.更新数据
        boolean success = lambdaUpdate()
                .set(LearningRecord::getMoment, recordDTO.getMoment())
                .set(finished, LearningRecord::getFinished, true)
                .set(finished, LearningRecord::getFinishTime, recordDTO.getCommitTime())
                .eq(LearningRecord::getId, old.getId())
                .update();
        if (!success) {
            throw new DbException("更新学习记录失败！");
        }
        return finished;
    }

    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        return lambdaQuery()
                .eq(LearningRecord::getLessonId, lessonId)
                .eq(LearningRecord::getSectionId, sectionId)
                .one();
    }

    private boolean handleExamRecord(Long userId, LearningRecordFormDTO recordDTO) {
        // 1.转换DTO为PO
        LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
        // 2.填充数据
        record.setUserId(userId);
        record.setFinished(true);
        record.setFinishTime(recordDTO.getCommitTime());
        // 3.写入数据库
        boolean success = save(record);
        if (!success) {
            throw new DbException("新增考试记录失败！");
        }
        return true;
    }
}
